package edu.bu.cs511.p5;

/**
 * edu.bu.cs511.p5.SchedTask class.  SchedTasks are the task
 * representation of schedulers.  As viewed from the schedulers that
 * are their parents, they are simply tasks.  As viewed by the tasks
 * they schedule, they are schedulers.  <p/>
 *
 * All nodes in the scheduler hierarchy (which is a Composite design
 * pattern) that are Components (of the composite design pattern) are
 * SchedTasks.  They signify the non-leaf nodes.<p/>
 *
 * SchedTasks have a scheduler associated with them conforming to the
 * IScheduler interface.  This scheduler is the Visitor part of the
 * composite pattern, and decides which task is to be executed next
 * every time this scheduler is invoked.<p/>
 *
 * 2/16/06, Gabriel Parmer: initial framework <p/>
 * 
 * 2/22/06, Gabriel Parmer: added endTask and concurrency
 */
public class SchedTask extends Task {
    private IScheduler scheduler = null;
    private int currentConcurrencyLevel = 0;

    /**
     * Constructor to create a scheduler task.<p/>
     *
     * @REQUIRES sched is the scheduler conforming to the IScheduler
     * interface which will make the scheduling decisions for this
     * task.  It follows the Visitor pattern.  parent is this tasks
     * parent scheduler.<p/>
     *
     * @MODIFIES sets the scheduler for this task to be sched and the
     * parent scheduler to parent.  Note that the invariant that all
     * components must be SchedTasks is maintained via typing here.
     */
    public SchedTask(IScheduler sched, SchedTask parent) {
        super(parent);
        setScheduler(sched);
    }

    /**
     * Constructor to create a scheduler task.<p/>
     *
     * @REQUIRES sched is the scheduler conforming to the IScheduler
     * interface which will make the scheduling decisions for this
     * task.  It follows the Visitor pattern.  The parent of this task
     * is defaulted to null.<p/>
     *
     * @MODIFIES sets the scheduler for this task to be sched.
     */
    public SchedTask(IScheduler sched) {
        /* must be the parent scheduler */
        this(sched, null);
        setScheduler(sched);
    }

    /**
     * Assign the scheduling policy for this task.<p/>
     *
     * @REQUIRES sched is the scheduling policy that conforms to the
     * IScheduler interface.<p/>
     *
     * @MODIFIES Sets this task to be associated with and dependent on
     * the sched scheduler.
     */
    public void setScheduler(IScheduler sched) {
        if (sched == null) return;

        scheduler = sched;
    }

    /**
     * Return the scheduler associated with this task.<p/>
     *
     * @EFFECTS Return the scheduler this task is dependent on.
     */
    public IScheduler getScheduler() {
        return scheduler;
    }

    /**
     * When this task is activated, do all that is involved in
     * scheduling and running a task.  Run the task and tell the
     * scheduler with it is done, also informing it if that task
     * blocked (is not long-running).  This will pass execution up the
     * hierarchy to its children tasks.
     *
     * @MODIFIES Invoke the scheduler to decide which child task to
     * run next, execute that task, and then inform the scheduler when
     * the task has finished.
     *
     * @EFFECTS Return if the scheduler is done with computation and
     * should "block."  Schedulers never block (however their
     * concurrency level can go to 0 in that their children do not
     * request any threads.)
     */
    public boolean executeTask() {
        Task next;
        boolean blocked;

	/* 
	 * this could happen if we are removing the task and the
	 * scheduler is deallocated 
	 */
        if (scheduler == null) return false;

        next = scheduler.schedule();
        if (next == null) return false;

        if (next.isTerminated()) {
            terminateChild(next);
            return true;
        }

        printDebug(getName());

	blocked = next.executeTask();

        scheduler.jobFinished(next, blocked);

	/* 
	 * schedulers should never block, but their requested
	 * concurrency might go to zero! 
	 */
        return false;
    }

    /** 
     * Terminate this task and all of its children.<p/>
     *
     * @MODIFIES Terminate this task such that it will not make any
     * more concurrency requests to its parent, and will, in fact, not
     * maintain a parent any longer.
     */
    public void endTask() {
        SchedTask parent = getParentScheduler();

        scheduler.terminateScheduler();
        setScheduler(null);

        setParentSchedTask(null);
        parent.terminateChild(this);
    }

    /**
     * Terminate a specific child.<p/>
     *
     * Assumption: must kill off all children of a parent before killing the
     * parent.<p/>
     *
     * @REQUIRES child that must be scheduled by this task that is to
     * be terminated.<p/>
     *
     * @MODIFIES Remove child from the scheduler's datastructures, and
     * update the concurrency level of this scheduler, passing that
     * value to our parent (recursively).
     */
    public void terminateChild(Task child) {
        Task parent = getParentScheduler();

        scheduler.removeTask(child);

        // update the concurrency now that we have removed the task
        currentConcurrencyLevel = scheduler.getThisConcurrencyLevel();

        // and pass that concurrency down the hierarchy to the root
        if (parent != null) {
            parent.childConcurrencyLevel(this, currentConcurrencyLevel);
        }
    }

    /**
     * This is a call from our child specifying its concurrency
     * level. Update the concurrency level of this scheduler given the
     * new concurrency value of the child, and pass it to our parent
     * (recursively).  <p/>
     *
     * @REQUIRES child must be a child scheduled by this SchedTasks's
     * scheduler, and concurrency must be the value of that child's
     * concurrency.<p/>
     *
     * @MODIFIES Update the child's concurrency level to be
     * concurrency, compute the concurrency level of task, and call
     * the same method in our parent, updating our concurrency.
     */
    protected void childConcurrencyLevel(Task child, int concurrency) {
        Task parent = getParentScheduler();
        int ourConcurrency;

        // ask the scheduler for our concurrency level
        ourConcurrency = scheduler.setChildConcurrencyLevel(child, concurrency);
        currentConcurrencyLevel = ourConcurrency;

        // and pass the concurrency level towards the root
        if (parent != null) {
            parent.childConcurrencyLevel(this, ourConcurrency);
        } else {
            System.out.println("DEBUG: concurrency seen at root is " + ourConcurrency);
        }
    }
}
